#pragma once

#include "../commons/utils.hpp"
#include "accept_char.hpp"
#include <iostream>
#include <unordered_map>
#include <vector>
#include <set>

using std::set;
using std::unordered_map;

/**
 * Transition graph shared by all FSAs
 */
class FsaSharedField {
private:
    vector<acceptableChars> _symbols;      // charset of the regex
    vector<vector<set<int>>> _states; // [from, symbol, to] matrix

public:
    FsaSharedField() { 
        _symbols.push_back({__EPSILON});
        _symbols.push_back({__WHITE_SPACES});
    }
    /**
     * Accept a char and transit to another state,
     * @return transited state, return std::nullopt if there is no such transition
     */ 
    optional<int> transit(int state, char ch) const {
        if (ch == __FAKE_END) {
            return std::nullopt;
        }
        for (int i = 0; i < _symbols.size(); ++i) {
            if(_symbols[i].match(ch)){
                if(_states.at(state)[i].empty()){
                    return std::nullopt;
                }else {
                    return *_states.at(state)[i].begin();
                }
            }
        }
        throw ParseException("No such char in the DFA's charset!");
    }
    /**
     * Check if the char should be consumed
     */ 
    bool advance(int state, char ch) const {
        if (ch == __FAKE_END) {
            return false;
        }
        for (int i = 0; i < _symbols.size(); ++i) {
            if (_symbols[i].match(ch)) {
                return !_states.at(state)[i].empty();
            }
        }
        throw ParseException("No such char in the DFA's charset!");
    }
    void clear() {
        _symbols.clear();
        _states.clear();
        _symbols.push_back({__EPSILON});
        _symbols.push_back({__WHITE_SPACES});
    }
    int symSize() const noexcept { return _symbols.size(); }
    int stateSize() const noexcept { return _states.size(); }
    vector<set<int>> &stateAt(int row) { return _states.at(row); }
    acceptableChars &symAt(int idx) { return _symbols.at(idx); }
    void newSymbol(const acceptableChars &sym) {
        _symbols.push_back(sym);
        for (auto &i : _states) {
            i.push_back({});
        }
    }
    void newState() { _states.push_back(vector<set<int>>(_symbols.size())); }
    void redirect(int from, int oldTo, int newTo) {
        for (auto &tos : _states.at(from)) {
            if (tos.count(oldTo)) {
                tos.erase(oldTo);
                tos.insert(newTo);
            }
        }
    }
    void addTransition(int from, int to, set<int> symbols) {
        for (auto sym : symbols) {
            _states.at(from).at(sym).insert(to);
        }
    }
    void cpAndMergeState(int sourceIdx, int targetIdx) {
        for (int i = 0; i < _symbols.size(); ++i) {
            _states.at(targetIdx)[i].insert(_states.at(sourceIdx)[i].begin(),
                                            _states.at(sourceIdx)[i].end());
        }
    }
    void eraseStates(int firstPos, int lastPos) {
        if (firstPos < 0 || firstPos > lastPos || lastPos > _states.size()) {
            throw std::range_error("Index out of range");
        }
        auto first = _states.begin() + firstPos;
        auto last = _states.begin() + lastPos;
        _states.erase(first, last);
    }

    void printStates() {
        for (int i = 0; i < stateSize(); ++i) {
            std::cout << "State " << i << ": ";
            unordered_map<int, unordered_set<int>> mp;
            for (int symIdx = 0; symIdx < _symbols.size(); symIdx++) {
                for (auto to : _states.at(i)[symIdx]) {
                    mp[to].insert(symIdx);
                }
            }
            for (auto &[to, syms] : mp) {
                std::cout << "to " << to << ": ";
                for (auto s : syms) {
                    // if(_symbols.at(s).size()>100){
                    //     std::cout << "any, ";
                    //     continue;
                    // }
                    for (auto ch : _symbols.at(s)) {
                        if (!ch.has_value()) {
                            std::cout << "?, ";
                        } else {
                            std::cout << ch.value() << ", ";
                        }
                    }
                }
            }
            std::cout << std::endl;
        }
    }

public:
    const vector<acceptableChars>& getSymbols() const { return _symbols; }
    const vector<vector<set<int>>>& getStates() const { return  _states; }
};
